package com.link.gateway.core.utils;

import com.link.gateway.core.cllog.LCLogger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

public class PasswordUtil {

    private PasswordUtil() {
    }

    private static boolean initialized = false;

    private static final Logger logger = LogManager.getLogger(PasswordUtil.class);

    /**
     * RSA
     * 字节数据转字符串专用集合
     */
    private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private static final String PRIVATE_KEY = "rsa/pkcs8_rsa_private_key.pem";

    private static final String PUBLIC_KEY = "rsa/rsa_public_key.pem";

    private static byte[] RSAcodes = new byte[256];

    static {
        for (int i = 0; i < 256; i++) {
            RSAcodes[i] = -1;
        }
        for (int i = 'A'; i <= 'Z'; i++) {
            RSAcodes[i] = (byte) (i - 'A');
        }

        for (int i = 'a'; i <= 'z'; i++) {
            RSAcodes[i] = (byte) (26 + i - 'a');
        }
        for (int i = '0'; i <= '9'; i++) {
            RSAcodes[i] = (byte) (52 + i - '0');
        }
        RSAcodes['+'] = 62;
        RSAcodes['/'] = 63;
    }



    public static String getMd5(String param) {
        String result = "Md5 exception";
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update((param).getBytes("UTF-8"));
            byte[] b = md5.digest();
            int i;
            StringBuffer buf = new StringBuffer("");

            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0) {
                    i += 256;
                }
                if (i < 16) {
                    buf.append("0");
                }
                buf.append(Integer.toHexString(i));
            }
            result = buf.toString().toUpperCase();
        } catch (NoSuchAlgorithmException e) {
            LCLogger.withException(e.getMessage());
        } catch (UnsupportedEncodingException e) {
            LCLogger.withException(e.getMessage());
        }
        return result;
    }


    /**
     * 从文件中输入流中加载公钥
     *
     * @param path 公钥输入流
     * @throws Exception 加载公钥时产生的异常
     */
    public static String loadPublicKeyByFile(String path) throws Exception {
        try (BufferedReader br = new BufferedReader(new FileReader(path + PUBLIC_KEY))) {
            String readLine = null;
            StringBuilder sb = new StringBuilder();
            while ((readLine = br.readLine()) != null) {
                if (readLine.charAt(0) == '-') {
                    continue;
                } else {
                    sb.append(readLine);
                    sb.append('\r');
                }
            }
            br.close();
            return sb.toString();
        } catch (IOException e) {
            throw new Exception("公钥数据流读取错误");
        } catch (NullPointerException e) {
            throw new Exception("公钥输入流为空");
        }
    }

    public static String loadPublicKeyStream() throws Exception {
        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            InputStream inputStream = classLoader.getResourceAsStream(PUBLIC_KEY);
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));

            String readLine = null;
            StringBuilder sb = new StringBuilder();
            while ((readLine = br.readLine()) != null) {
                if (readLine.charAt(0) == '-') {
                    continue;
                } else {
                    sb.append(readLine);
                    sb.append('\r');
                }
                System.out.println(readLine);
            }
            return sb.toString();
        } catch (IOException e) {
            throw new Exception("公钥数据读取错误");
        } catch (NullPointerException e) {
            throw new Exception("公钥输入流为空");
        }
    }

    /**
     * 从字符串中加载公钥
     *
     * @throws Exception 加载公钥时产生的异常
     */
    public static RSAPublicKey loadPublicKeyByStr() throws Exception {
        try {
            Resource resource = new ClassPathResource(PUBLIC_KEY);
            BASE64Decoder base64 = new BASE64Decoder();
            byte[] buffer = base64.decodeBuffer(((ClassPathResource) resource).getPath());
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }

    /**
     * 从文件中加载私钥
     *
     * @return 是否成功
     * @throws Exception
     */
    public static String loadPrivateKeyByFile() throws Exception {
        try {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            InputStream inputStream = classLoader.getResourceAsStream(PRIVATE_KEY);
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));

            String readLine = null;
            StringBuilder sb = new StringBuilder();
            while ((readLine = br.readLine()) != null) {
                if (readLine.charAt(0) == '-') {
                    continue;
                } else {
                    sb.append(readLine);
                    sb.append('\r');
                }
            }
            br.close();
            return sb.toString();
        } catch (IOException e) {
            throw new Exception("私钥数据读取错误");
        } catch (NullPointerException e) {
            throw new Exception("私钥输入流为空");
        }
    }

    public static RSAPrivateKey loadPrivateKeyByStr(String privateKeyStr)
            throws Exception {
        try {
            BASE64Decoder base64Decoder = new BASE64Decoder();
            byte[] buffer = base64Decoder.decodeBuffer(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("私钥非法");
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空");
        }
    }

    /**
     * 公钥加密过程
     *
     * @param publicKey     公钥
     * @param plainTextData 明文数据
     * @return
     * @throws Exception 加密过程中的异常信息
     */
    public static byte[] encryptPublicKey(RSAPublicKey publicKey, byte[] plainTextData)
            throws Exception {
        if (publicKey == null) {
            throw new Exception("加密公钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] output = cipher.doFinal(plainTextData);
            return output;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此加密算法");
        } catch (NoSuchPaddingException e) {
            logger.error("PasswordUtil捕获公钥加密过程中的异常: ",e);
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("加密公钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("明文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("明文数据已损坏");
        }
    }

    /**
     * 私钥加密过程
     *
     * @param privateKey    私钥
     * @param plainTextData 明文数据
     * @return
     * @throws Exception 加密过程中的异常信息
     */
    public static byte[] encryptPrivateKey(RSAPrivateKey privateKey, byte[] plainTextData)
            throws Exception {
        if (privateKey == null) {
            throw new Exception("加密私钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            byte[] output = cipher.doFinal(plainTextData);
            return output;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此加密算法");
        } catch (NoSuchPaddingException e) {
            logger.error("PasswordUtil捕获私钥加密过程中的异常: ",e);
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("加密私钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("明文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("明文数据已损坏");
        }
    }

    /**
     * 私钥解密过程
     *
     * @param privateKey 私钥
     * @param cipherData 密文数据
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static byte[] decryptPrivateKey(RSAPrivateKey privateKey, byte[] cipherData)
            throws Exception {
        if (privateKey == null) {
            throw new Exception("解密私钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] output = cipher.doFinal(cipherData);
            return output;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此解密算法");
        } catch (NoSuchPaddingException e) {
            logger.error("PasswordUtil捕获私钥解密过程中的异常: ",e);
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("解密私钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("密文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("密文数据已损坏");
        }
    }

    /**
     * 公钥解密过程
     *
     * @param publicKey  公钥
     * @param cipherData 密文数据
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static byte[] decryptPublicKey(RSAPublicKey publicKey, byte[] cipherData)
            throws Exception {
        if (publicKey == null) {
            throw new Exception("解密公钥为空, 请设置");
        }
        Cipher cipher = null;
        try {
            // 使用默认RSA
            cipher = Cipher.getInstance("RSA");
            // cipher= Cipher.getInstance("RSA", new BouncyCastleProvider());
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            byte[] output = cipher.doFinal(cipherData);
            return output;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此解密算法");
        } catch (NoSuchPaddingException e) {
            logger.error("PasswordUtil捕获公钥解密过程中的异常: ",e);
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("解密公钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("密文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("密文数据已损坏");
        }
    }

    /**
     * 字节数据转十六进制字符串
     *
     * @param data 输入数据
     * @return 十六进制内容
     */
    public static String byteArrayToString(byte[] data) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < data.length; i++) {
            // 取出字节的高四位 作为索引得到相应的十六进制标识符 注意无符号右移
            stringBuilder.append(HEX_CHAR[(data[i] & 0xf0) >>> 4]);
            // 取出字节的低四位 作为索引得到相应的十六进制标识符
            stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]);
            if (i < data.length - 1) {
                stringBuilder.append(' ');
            }
        }
        return stringBuilder.toString();
    }

    public static String decryptString(String str) throws Exception {
        RSAPrivateKey privateKey = loadPrivateKeyByStr(loadPrivateKeyByFile());
        byte[] res = decryptPrivateKey(privateKey, RsadecodeStr(str));
        return new String(res);
    }

    public static byte[] RsadecodeStr(String data) {
        return RsadecodeChars(data.toCharArray());
    }

    public static byte[] RsadecodeChars(char[] data) {

        int tempLen = data.length;
        for (int ix = 0; ix < data.length; ix++) {
            if ((data[ix] > 255) || RSAcodes[data[ix]] < 0) {
                --tempLen;
            }
        }

        int len = (tempLen / 4) * 3;
        if ((tempLen % 4) == 3) {
            len += 2;
        }
        if ((tempLen % 4) == 2) {
            len += 1;

        }
        byte[] out = new byte[len];

        int shift = 0; // # of excess bits stored in accum
        int accum = 0; // excess bits
        int index = 0;

        // we now go through the entire array (NOT using the 'tempLen' value)
        for (int ix = 0; ix < data.length; ix++) {
            int value = (data[ix] > 255) ? -1 : RSAcodes[data[ix]];

            if (value >= 0) { // skip over non-code
                accum <<= 6; // bits shift up by 6 each time thru
                shift += 6; // loop, with new bits being put in
                accum |= value; // at the bottom.
                if (shift >= 8) { // whenever there are 8 or more shifted in,
                    shift -= 8; // write them out (from the top, leaving any
                    out[index++] = // excess at the bottom for next iteration.
                            (byte) ((accum >> shift) & 0xff);
                }
            }
        }

        // if there is STILL something wrong we just have to throw up now!
        if (index != out.length) {
            throw new Error("Miscalculated data length (wrote " + index
                    + " instead of " + out.length + ")");
        }

        return out;
    }

    /**
     * 采用32位MD5为用户密码加密
     *
     * @param pwd 需要加密的字符串
     * @return 加密后的字符串
     */
    public static String md5(String pwd) {
        String md5 = "";
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(pwd.getBytes());
            byte[] b = md.digest();
            int count;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                count = b[offset];
                if (count < 0) {
                    count += 256;
                }
                if (count < 16) {
                    buf.append("0");
                }
                buf.append(Integer.toHexString(count));
            }
            md5 = buf.toString();
        } catch (NoSuchAlgorithmException e) {
            logger.error("PasswordUtil捕获异常: ",e);
        }
        return md5;
    }


    /**
     * 使用md5的算法进行加密
     */
    public static String parseStrToMd5L32(String str) {
        String reStr = null;
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] bytes = md5.digest(str.getBytes());
            StringBuffer stringBuffer = new StringBuffer();
            for (byte b : bytes) {
                int bt = b & 0xff;
                if (bt < 16) {
                    stringBuffer.append(0);
                }
                stringBuffer.append(Integer.toHexString(bt));
            }
            reStr = stringBuffer.toString();
        } catch (NoSuchAlgorithmException e) {
            logger.error("PasswordUtil捕获异常: ",e);
        }
        return reStr;
    }

    /**
     * 功能：Base64
     * @author jiangshuai
     * @date 2016年10月03日
     */
    // 加密
    public static String getBase64Encoder(String str) {
        byte[] bytes = null;
        String temp = null;
        try {
            bytes = str.getBytes("utf-8");
        } catch (Exception e) {
            logger.error("PasswordUtil捕获异常: ",e);
        }
        if (bytes != null) {
            temp = new BASE64Encoder().encode(bytes);
        }
        return temp;
    }

    // 解密
    public static String getBase64Decoder(String str) {
        byte[] byteStr = null;
        String result = null;
        if (str != null) {
            BASE64Decoder decoder = new BASE64Decoder();
            try {
                byteStr = decoder.decodeBuffer(str);
                result = new String(byteStr, "utf-8");
            } catch (Exception e) {
                logger.error("PasswordUtil捕获异常: ",e);
            }
        }
        return result;
    }

    /**
     * 功能：编码字符串
     * @author jiangshuai
     * @param data 源字符串
     * @return String
     * @date 2016年10月03日
     */
    public static String encodeStr(String data) {
        return new String(encodeBytes(data.getBytes()));
    }

    /**
     * 功能：解码字符串
     * @author jiangshuai
     * @param data 源字符串
     * @return String
     * @date 2016年10月03日
     */
    public static byte[] decodeStr(String data) {
        return decodeChars(data.toCharArray());
    }

    /**
     * 功能：编码byte[]
     * @author jiangshuai
     * @param data 源
     * @return char[]
     * @date 2016年10月03日
     */
    public static char[] encodeBytes(byte[] data) {
        char[] out = new char[((data.length + 2) / 3) * 4];
        for (int i = 0, index = 0; i < data.length; i += 3, index += 4) {
            boolean quad = false;
            boolean trip = false;

            int val = (0xFF & (int) data[i]);
            val <<= 8;
            if ((i + 1) < data.length) {
                val |= (0xFF & (int) data[i + 1]);
                trip = true;
            }
            val <<= 8;
            if ((i + 2) < data.length) {
                val |= (0xFF & (int) data[i + 2]);
                quad = true;
            }
            out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
            val >>= 6;
            out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
            val >>= 6;
            out[index + 1] = alphabet[val & 0x3F];
            val >>= 6;
            out[index + 0] = alphabet[val & 0x3F];
        }
        return out;
    }

    /**
     * 功能：解码
     * @author jiangshuai
     * @param data 编码后的字符数组
     * @return byte[]
     * @date 2016年10月03日
     */
    public static byte[] decodeChars(char[] data) {

        int tempLen = data.length;
        for (int ix = 0; ix < data.length; ix++) {
            if ((data[ix] > 255) || codes[data[ix]] < 0) {
                --tempLen; // ignore non-valid chars and padding
            }
        }
        // calculate required length:
        // -- 3 bytes for every 4 valid base64 chars
        // -- plus 2 bytes if there are 3 extra base64 chars,
        // or plus 1 byte if there are 2 extra.

        int len = (tempLen / 4) * 3;
        if ((tempLen % 4) == 3) {
            len += 2;
        }
        if ((tempLen % 4) == 2) {
            len += 1;

        }
        byte[] out = new byte[len];

        int shift = 0; // # of excess bits stored in accum
        int accum = 0; // excess bits
        int index = 0;

        // we now go through the entire array (NOT using the 'tempLen' value)
        for (int ix = 0; ix < data.length; ix++) {
            int value = (data[ix] > 255) ? -1 : codes[data[ix]];

            if (value >= 0) { // skip over non-code
                accum <<= 6; // bits shift up by 6 each time thru
                shift += 6; // loop, with new bits being put in
                accum |= value; // at the bottom.
                if (shift >= 8) { // whenever there are 8 or more shifted in,
                    shift -= 8; // write them out (from the top, leaving any
                    out[index++] = // excess at the bottom for next iteration.
                            (byte) ((accum >> shift) & 0xff);
                }
            }
        }

        // if there is STILL something wrong we just have to throw up now!
        if (index != out.length) {
            throw new Error("Miscalculated data length (wrote " + index
                    + " instead of " + out.length + ")");
        }

        return out;
    }

    /**
     * @author jiangshuai
     * 功能：编码文件
     * @param file 源文件
     * @date 2016年10月03日
     */
    public static void encodeFile(File file) throws IOException {
        if (!file.exists()) {
            System.exit(0);
        } else {
            byte[] decoded = readBytes(file);
            if (encodeBytes(decoded) != null) {
                char[] encoded = encodeBytes(decoded);
                writeChars(file, encoded);
            }
        }
        file = null;
    }

    /**
     * 功能：解码文件。
     * @author jiangshuai
     * @param file 源文件
     * @throws IOException
     * @date 2016年10月03日
     */
    public static void decodeFile(File file) throws IOException {
        if (!file.exists()) {
            System.exit(0);
        } else {
            char[] encoded = readChars(file);
            byte[] decoded = decodeChars(encoded);
            writeBytes(file, decoded);
        }
        file = null;
    }

    //
    // code characters for values 0..63
    //
    private static char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
            .toCharArray();

    //
    // lookup table for converting base64 characters to value in range 0..63
    //
    private static byte[] codes = new byte[256];

    static {
        for (int i = 0; i < 256; i++) {
            codes[i] = -1;
            // LoggerUtil.debug(i + "&" + codes[i] + " ");
        }
        for (int i = 'A'; i <= 'Z'; i++) {
            codes[i] = (byte) (i - 'A');
            // LoggerUtil.debug(i + "&" + codes[i] + " ");
        }

        for (int i = 'a'; i <= 'z'; i++) {
            codes[i] = (byte) (26 + i - 'a');
            // LoggerUtil.debug(i + "&" + codes[i] + " ");
        }
        for (int i = '0'; i <= '9'; i++) {
            codes[i] = (byte) (52 + i - '0');
            // LoggerUtil.debug(i + "&" + codes[i] + " ");
        }
        codes['+'] = 62;
        codes['/'] = 63;
    }

    private static byte[] readBytes(File file) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] b = null;
        try (InputStream fis = new FileInputStream(file);
             InputStream is = new BufferedInputStream(fis)) {
            int count = 0;
            byte[] buf = new byte[16384];
            while ((count = is.read(buf)) != -1) {
                if (count > 0) {
                    baos.write(buf, 0, count);
                }
            }
            b = baos.toByteArray();

        }

        return b;
    }

    private static char[] readChars(File file) throws IOException {
        CharArrayWriter caw = new CharArrayWriter();
        try (Reader fr = new FileReader(file);
             Reader in = new BufferedReader(fr)) {
            int count = 0;
            char[] buf = new char[16384];
            while ((count = in.read(buf)) != -1) {
                if (count > 0) {
                    caw.write(buf, 0, count);
                }
            }

        } catch (Exception e) {
            logger.error("PasswordUtil捕获异常: ",e);
        }

        return caw.toCharArray();
    }

    private static void writeBytes(File file, byte[] data) throws IOException {
        OutputStream fos = null;
        OutputStream os = null;
        try {
            fos = new FileOutputStream(file);
            os = new BufferedOutputStream(fos);
            os.write(data);

        } finally {
            try {
                if (os != null) {
                    os.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }

    private static void writeChars(File file, char[] data) throws IOException {
        Writer fos = null;
        Writer os = null;
        try {
            fos = new FileWriter(file);
            os = new BufferedWriter(fos);
            os.write(data);

        } finally {
            try {
                if (os != null) {
                    os.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (Exception e) {
                logger.error("PasswordUtil捕获异常: ",e);
            }
        }
    }


//    public static byte[] encrypt(byte[] content, byte[] keyBytes, byte[] ivBytes){
//        if (!initialized) {
//            Security.addProvider(new BouncyCastleProvider());
//            initialized = true;
//        }
//
//
//    }
    /**
     * 算法名称
     */
    private static final String KEY_ALGORITHM = "AES";
    /**
     * 加解密算法/模式/填充方式
     */
    private static final String ALGORITHM = "AES/CBC/PKCS7Padding";
    private static Cipher cipher;
    private static Key key;

    public static void init(byte[] keyBytes) {
        // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
        int base = 16;
        if (keyBytes.length % base != 0) {
            int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
            keyBytes = temp;
        }
        if (!initialized) {
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            initialized = true;
        }
        // 转化成JAVA的密钥格式
        key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
        try {
            // 初始化cipher
            cipher = Cipher.getInstance(ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            logger.error("PasswordUtil捕获异常: ",e);
        } catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            logger.error("PasswordUtil捕获异常: ",e);
        }
    }

    public static byte[] encrypt(byte[] content, byte[] keyBytes, byte[] ivBytes) {
        byte[] encryptedText = null;
        try {
            init(keyBytes);
            cipher.init(Cipher.ENCRYPT_MODE, key, generateIV(ivBytes));
            encryptedText = cipher.doFinal(content);
        } catch (IllegalBlockSizeException e) {
            logger.error("PasswordUtil捕获异常: ",e);
        } catch (BadPaddingException e) {
            logger.error("PasswordUtil捕获异常: ",e);
        } catch (InvalidKeyException e) {
            logger.error("PasswordUtil捕获异常: ",e);
        } catch (InvalidAlgorithmParameterException e) {
            logger.error("PasswordUtil捕获异常: ",e);
        } catch (Exception e) {
            logger.error("PasswordUtil捕获异常: ",e);
        }
        return encryptedText;
    }


    /**
     *  JAVA Cipher 解密
     *  作用域: 微信小程序解密用，小程序本身加密，需要用此方法进行解密
     *  引入方式: Maven引入 <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk16</artifactId> <version>1.46</version>
     * @param content 密文
     */
    public static byte[] decrypt(byte[] encryptedData, byte[] keyBytes, byte[] ivBytes) throws InvalidAlgorithmParameterException {
        byte[] encryptedText = null;
        try {
            init(keyBytes);
            // 初始化
            cipher.init(Cipher.DECRYPT_MODE, key, generateIV(ivBytes));
            encryptedText = cipher.doFinal(encryptedData);
        } catch (NoSuchAlgorithmException e) {
            logger.error("PasswordUtil捕获NoSuchAlgorithmException异常: ",e);
        } catch (NoSuchPaddingException e) {
            logger.error("PasswordUtil捕获NoSuchPaddingException异常: ",e);
        } catch (InvalidKeyException e) {
            logger.error("PasswordUtil捕获InvalidKeyException异常: ",e);
        } catch (IllegalBlockSizeException e) {
            logger.error("PasswordUtil捕获IllegalBlockSizeException异常: ",e);
        } catch (BadPaddingException e) {
            logger.error("PasswordUtil捕获BadPaddingException异常: ",e);
        } catch (NoSuchProviderException e) {
            logger.error("PasswordUtil捕获NoSuchProviderException异常: ",e);
        } catch (Exception e) {
            logger.error("PasswordUtil捕获异常: ",e);
        }
        return encryptedText;
    }


    /**
     * 生成iv
     * */
    public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
        AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
        params.init(new IvParameterSpec(iv));
        return params;
    }


    /**
     * 利用java原生的摘要实现SHA256加密
     *
     * @param str 加密后的报文
     * @return
     */
    public static String getSHA256StrJava(String str) {
        MessageDigest messageDigest;
        String encodeStr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(str.getBytes("UTF-8"));
            encodeStr = byte2Hex(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            logger.error("PasswordUtil捕获NoSuchAlgorithmException异常: ",e);
        } catch (UnsupportedEncodingException e) {
            logger.error("PasswordUtil捕获UnsupportedEncodingException异常: ",e);
        }
        return encodeStr;
    }

    public static String getMD5StrJava(String str) {
        MessageDigest messageDigest;
        String encodeStr = "";
        try {
            messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(str.getBytes("UTF-8"));
            encodeStr = byte2Hex(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            logger.error("NoSuchAlgorithmException: ",e);
        } catch (UnsupportedEncodingException e) {
            logger.error("PasswordUtil捕获UnsupportedEncodingException异常: ",e);
        }
        return encodeStr;
    }

    /**
     * 将byte转为16进制
     *
     * @param bytes
     * @return
     */
    private static String byte2Hex(byte[] bytes) {
        StringBuffer stringBuffer = new StringBuffer();
        String temp = null;
        for (int i = 0; i < bytes.length; i++) {
            temp = Integer.toHexString(bytes[i] & 0xFF);
            if (temp.length() == 1) {
                //1得到一位的进行补0操作
                stringBuffer.append("0");
            }
            stringBuffer.append(temp);
        }
        return stringBuffer.toString();
    }

    /**
     * <p>本系统加密方法</p>
     *
     * @author yrf
     * @param str
     * @return
     */
    public static String generatePwd(String str) {
        if (str == null || "".equals(str)) {
            return null;
        }
        str = "123@!LINK" + str + "CRM";
        String shaStr = getSHA256StrJava(str);
        String md5Str = getMD5StrJava(shaStr);
        String result = "{LINKSHA}" + md5Str;
        return result;
    }

    /**
     * <p>比较密码明文加密后是否等于该密文</p>
     *
     * @author yrf
     * @param pwd       明文
     * @param encodeStr 密文
     * @return
     */
    public static boolean isEquals(String pwd, String encodeStr) {
        if (pwd == null || encodeStr == null) {
            return false;
        }
        if (!encodeStr.startsWith("{LINKSHA}")) {
            //throw new IllegalStateException("");
            return false;
        }
        pwd = generatePwd(pwd);
        if (pwd != null) {
            return pwd.equals(encodeStr);
        } else {
            return false;
        }
    }


    /**
     * SHA-1 加密字符串
     *
     * @return
     */
    public static String encryptBySHA1(String strSrc) {
        MessageDigest md = null;
        String strDes = null;
        byte[] bt = strSrc.getBytes();
        try {
            md = MessageDigest.getInstance("SHA-1");
            md.update(bt);
            strDes = StringUtils.bytes2Hex(md.digest()); // to HexString
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
        return strDes;
    }


    /**
     * MD5 加密字符串
     *
     * @return
     */
    public static String encryptByMD5(String strSrc) {
        StringBuilder sb = new StringBuilder();
        MessageDigest md5;
        try {
            md5 = MessageDigest.getInstance("MD5");
            md5.update(strSrc.getBytes());
            for (byte b : md5.digest()) {
                sb.append(String.format("%02X", b)); // 10进制转16进制，X 表示以十六进制形式输出，02 表示不足两位前面补0输出
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            logger.error("StringUtils捕获异常: ",e);
        }
        return null;
    }
}
